﻿using System;
using System.Diagnostics;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace VRacer
{
    public class Car : UserControl
    {
        public Car()
        {
            
            IsCPU = false;
        }

        private bool _isCpu = false;

        public bool IsCPU
        {
            get { return _isCpu; }
            set
            {
                BitmapImage carI = new BitmapImage();
                if (value)
                {
                    carI.UriSource = new Uri("car.PNG", UriKind.Relative);
                    Width = 20;
                    Height = 50;
                    Angle = 0;
                    var ct = new CompositeTransform();
                    ct.Rotation = 0;
                    RenderTransform = ct;
                    RenderTransformOrigin = new Point(.5,.5);
                }
                else
                {
                    carI.UriSource = new Uri("car2.PNG", UriKind.Relative);
                    Angle = 0;
                    Width = 20;
                    Height = 50;
                    var ct = new CompositeTransform();
                    ct.Rotation = 0;
                    RenderTransform = ct;
                    RenderTransformOrigin = new Point(.5, .5);
                }
                //Width = 100;
                //Height = 150;
                Image i = new Image();
                i.Source = carI;
                Content = i;
                _isCpu = value;
            }
        }

        public double Angle { get; set; }
        public double Speed { get; set; }
        public double CurrentEllipseRotation { get; set; }

        public double MaxDeltaFi
        {
            get
            {
                double step = (MaxTurnAngleMinSpeed - MaxTurnAngleMaxSpeed)/MaxSpeed;
                return -Speed*step + MaxTurnAngleMinSpeed;
            }
        }
        
        public double MaxTurnAngleMaxSpeed { get { return 15.0/ (180.0 / Math.PI); } }
        public double MaxTurnAngleMinSpeed { get { return 90.0 / (180.0 / Math.PI); } }
        
        public bool OutOfTrack { get; set; }
        public int MaxSpeed
        {
            get
            {
                if (!OutOfTrack) return 200;
                else 
                    return 10;
            }
        }

        public int MaxSpeedDeltaX
        {
            get { return 10; }
        }
        public int MaxSpeedDeltaY
        {
            get { return 10; }
        }

        private double _speedDeltaY = 0;
        public double SpeedDeltaY
        {
            get { return _speedDeltaY; }
            set
            {
                _speedDeltaY = value;
                if (_speedDeltaY > MaxSpeedDeltaY)
                    _speedDeltaY = MaxSpeedDeltaY;
                if (_speedDeltaY < -MaxSpeedDeltaY)
                    _speedDeltaY = -MaxSpeedDeltaY;
            }
        }

        private double _speedDeltaX = 0;

        public double SpeedDeltaX
        {
            get { return _speedDeltaX; }
            set
            {
                _speedDeltaX = value;
                if (_speedDeltaX > MaxSpeedDeltaX)
                    _speedDeltaX = MaxSpeedDeltaX;
                if (_speedDeltaX < -MaxSpeedDeltaX)
                    _speedDeltaX = -MaxSpeedDeltaX;
            }
        }

        public double PrevAngle { get; set; }
    

        public double Top
        {
            get { return (double) this.GetValue(Canvas.TopProperty); }
            set
            {
                //_prevPosY = (double)this.GetValue(Canvas.TopProperty) + this.Height/2;
                SetValue(Canvas.TopProperty, value);
            }
        }

        public double Left
        {
            get { return (double)this.GetValue(Canvas.LeftProperty); }
            set
            {
                //_prevPosX = (double) this.GetValue(Canvas.LeftProperty) + this.Width/2;
                SetValue(Canvas.LeftProperty, value);
            }
        }

        public double NextPosX
        {
            get
            {
                return CurPosX + (Speed + SpeedDelta)*Math.Sin(Math.PI + DeltaFi + Angle);
            }
        }

        public double NextPosY
        {
            get
            {   
                return this.CurPosY + (this.Speed + this.SpeedDelta)*Math.Cos(Math.PI + this.DeltaFi + this.Angle);
            }
        }

        public double CurPosX
        {
            get { return Left + Width/2; }
        }

        public double CurPosY
        {
            get { return Top + Height/2; }
        }
        

        private double _prevPosX = 0;
        public double PrevPosX { get { return _prevPosX; }
        set { _prevPosX = value; }}

        private double _prevPosY = 0;
        public double PrevPosY { get { return _prevPosY; } set { _prevPosY = value; }}

        public double DeltaFi
        {
            get
            {
                double _deltaFi;
                _deltaFi = Math.Atan2(SpeedDeltaX, SpeedDeltaY);
                if (_deltaFi >= Math.PI/2 && _deltaFi <= Math.PI)
                {
                    _deltaFi = Math.PI - _deltaFi;
                }
                if (_deltaFi > -Math.PI && _deltaFi < -Math.PI/2)
                {
                    _deltaFi = -Math.PI - _deltaFi;

                }
                if (MaxDeltaFi < _deltaFi && _deltaFi > 0)
                    return MaxDeltaFi;

                if (-MaxDeltaFi > _deltaFi && _deltaFi < 0)
                    return -MaxDeltaFi;
                return _deltaFi;
            }
        }

        private bool _breaking = false;
        public bool Breaking
        {
            get
            {
                _breaking = false;
                double _deltaFi;
                _deltaFi = Math.Atan2(SpeedDeltaX, SpeedDeltaY);
                if (_deltaFi >= Math.PI / 2 && _deltaFi <= Math.PI)
                {
                    _breaking = true;
                }
                if (_deltaFi > -Math.PI && _deltaFi < -Math.PI / 2)
                {
                    _breaking = true;
                }
                return _breaking;
            }
        }

        public double BreakingCoef { get { return 1.2; } } //it breaks faster than accelerates 0_o
        public double SpeedDelta
        {
            get
            {
                double res = (Breaking?-BreakingCoef:1.2) * Math.Sqrt(Math.Pow(this.SpeedDeltaX, 2) + Math.Pow(this.SpeedDeltaY, 2));   
                if (this.Speed + res < 0)
                    return 0;
                return res;
            }
        }

        public double InitPosX { get; set; }
        public double InitPosY { get; set; }

        public Ellipse elVelocity { get; set; }

        public void RotateEllipse(Storyboard sb, double fromAngle, double toAngle)
        {

            DoubleAnimationUsingKeyFrames da = new DoubleAnimationUsingKeyFrames();
            System.Windows.PropertyPath pp = new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationZ)");
            da.SetValue(Storyboard.TargetPropertyProperty, pp);
            Storyboard.SetTarget(da, elVelocity);
            EasingDoubleKeyFrame kf1 = new EasingDoubleKeyFrame();
            kf1.Value = fromAngle;
            kf1.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0));
            da.KeyFrames.Add(kf1);
            EasingDoubleKeyFrame kf2 = new EasingDoubleKeyFrame();
            kf2.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 1));
            kf2.Value = toAngle;
            da.KeyFrames.Add(kf2);
            sb.Children.Add(da);
        }

        public void MoveCarY(Storyboard sb, double fromPos, double toPos)
        {
            PrevPosY = CurPosY;
            DoubleAnimationUsingKeyFrames da = new DoubleAnimationUsingKeyFrames();
            System.Windows.PropertyPath pp = new PropertyPath("(Canvas.Top)");
            da.SetValue(Storyboard.TargetPropertyProperty, pp);
            Storyboard.SetTarget(da, this);
            EasingDoubleKeyFrame kf1 = new EasingDoubleKeyFrame();
            kf1.Value = fromPos;
            kf1.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0));
            da.KeyFrames.Add(kf1);
            EasingDoubleKeyFrame kf2 = new EasingDoubleKeyFrame();
            kf2.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 1));
            kf2.Value = toPos;
            var ease = new SineEase();
            ease.EasingMode = EasingMode.EaseIn;
            kf2.EasingFunction = ease;
            da.KeyFrames.Add(kf2);
            sb.Children.Add(da);

        }
        public void MoveCarX(Storyboard sb, double fromPos, double toPos)
        {
            PrevPosX = CurPosX;
            DoubleAnimationUsingKeyFrames da = new DoubleAnimationUsingKeyFrames();
            System.Windows.PropertyPath pp = new PropertyPath("(Canvas.Left)");
            da.SetValue(Storyboard.TargetPropertyProperty, pp);
            Storyboard.SetTarget(da, this);
            EasingDoubleKeyFrame kf1 = new EasingDoubleKeyFrame();
            kf1.Value = fromPos;
            kf1.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0));
            da.KeyFrames.Add(kf1);
            EasingDoubleKeyFrame kf2 = new EasingDoubleKeyFrame();
            kf2.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 1));
            kf2.Value = toPos;
            var ease = new SineEase();
            ease.EasingMode = EasingMode.EaseIn;
            kf2.EasingFunction = ease;
            da.KeyFrames.Add(kf2);
            sb.Children.Add(da);
        }

        public void RotateCar(Storyboard sb, double fromAngle, double toAngle)
        {

            DoubleAnimationUsingKeyFrames da = new DoubleAnimationUsingKeyFrames();
            System.Windows.PropertyPath pp = new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.Rotation)");
            da.SetValue(Storyboard.TargetPropertyProperty, pp);
            Storyboard.SetTarget(da, this);
            EasingDoubleKeyFrame kf1 = new EasingDoubleKeyFrame();
            kf1.Value = fromAngle;
            kf1.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0));
            da.KeyFrames.Add(kf1);
            EasingDoubleKeyFrame kf2 = new EasingDoubleKeyFrame();
            kf2.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 1));
            kf2.Value = toAngle;
            da.KeyFrames.Add(kf2);
            sb.Children.Add(da);
        }

        
        public void CreateStoryboardAndMakeNextStep(Storyboard sb)
        {
            this.Speed += this.SpeedDelta;
            if (this.Speed > this.MaxSpeed)
                this.Speed = this.MaxSpeed;

            this.MoveCarY(sb, Top, this.NextPosY - Height/2);
            this.MoveCarX(sb, Left, this.NextPosX - Width/2);

            double newAngle = -3 * Math.PI / 2 +
                              Math.Atan2((double)(this.NextPosY - this.CurPosY),
                                         (double)(this.NextPosX - this.CurPosX));
            if (Math.PI < Math.Abs(newAngle - this.PrevAngle))
            {
                if (newAngle > this.PrevAngle)
                    newAngle -= 2 * Math.PI;
                else
                    newAngle += 2 * Math.PI;
            }
            //if (Math.PI < Math.Abs(newAngle - this.PrevAngle))
            //    Debugger.Break();

            this.Angle = this.Angle + DeltaFi;//??/

            this.RotateCar(sb, this.PrevAngle * (180.0 / Math.PI), newAngle * (180.0 / Math.PI));

            this.PrevAngle = newAngle;
            RotateEllipse(sb, CurrentEllipseRotation, (int)(-this.PrevAngle * (180.0 / Math.PI)));
            CurrentEllipseRotation = (int)(-PrevAngle * (180.0 / Math.PI));
        }
    }
}
